package com.iot.tenant.service.impl;

import cn.hutool.core.lang.UUID;
import com.dc3.common.bean.R;
import com.iot.openfeign.UserClient;
import com.iot.tenant.pojo.Dc3Tenant;
import com.iot.tenant.pojo.Dc3TenantBind;
import com.iot.tenant.pojo.Dc3User;
import com.iot.tenant.service.TenantService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.relational.core.query.Update;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


/**
 * @Author 咕唧
 * @Date 2022/7/14 20:09
 * @Version 1.0
 */
@Service
@Transactional
public class TenantServiceImpl implements TenantService {

    private UserClient userClient;

    @Resource
    public void setUserClient(UserClient userClient) {
        this.userClient = userClient;
    }

    private R2dbcEntityTemplate r2dbcEntityTemplate;

    @Autowired
    public void setR2dbcEntityTemplate(R2dbcEntityTemplate r2dbcEntityTemplate) {
        this.r2dbcEntityTemplate = r2dbcEntityTemplate;
    }

    @Override
    public Mono<R<Object>> addTenant(Mono<Dc3Tenant> dc3Tenant) {
        //我们需要对数据进行校验
        //校验的方式有断言,抛出异常
        //我们通过响应式编程来判断, 响应式编程我们好像没有发现有断言功能
        //我们就转换思路,先过滤,因为过滤后如果不符合条件就没有数据,没有数据我们后面的操作都不执行,都不执行就是一个空的mono
        //如果是空的mono就代表着一定是数据有问题,我们就一切按照正常有数据的方式,最后设置一个如果为空就返回什么数据
//        return dc3Tenant
//                .filter(tenant -> StringUtils.hasText(tenant.getName()) && StringUtils.hasText(tenant.getDescription()))
//                .map(tenant -> {
//                    r2dbcEntityTemplate.insert(Dc3Tenant.class).into("dc3_tenant").using(tenant).subscribe();
//                    return R.ok();
//                }).defaultIfEmpty(R.fail("必要数据为空"));

        //用户id需要根据uuid自动生成，租户只需要传递用户名，介绍信息
        return dc3Tenant
                .filter(tenant -> StringUtils.hasText(tenant.getName()) && StringUtils.hasText(tenant.getDescription()))
                .doOnNext((dc3Tenant1) -> dc3Tenant1.setId(UUID.randomUUID().toString(true)))
                .flatMap(tenant -> r2dbcEntityTemplate.insert(Dc3Tenant.class).into("dc3_tenant").using(tenant)
                        .map(result -> R.ok()))
                .defaultIfEmpty(R.fail("必要数据为空"));
    }

    @Override
    public Mono<Dc3Tenant> findById(String id) {
        return r2dbcEntityTemplate.selectOne(Query.query(Criteria.where("id").is(id)), Dc3Tenant.class);
    }

    @Override
    public Flux<Dc3Tenant> findByName(String name) {
        return r2dbcEntityTemplate.select(Dc3Tenant.class)
                .from("dc3_tenant")
                .matching(Query.query(Criteria.where("name").is(name)))
                .all();

        // return r2dbcEntityTemplate.select(Query.query(Criteria.where("name").is(name)), Dc3Tenant.class);
    }

    @Override
    public Mono<R<Object>> updateTenant(Mono<Dc3Tenant> tenantMono) {

        //更新必须传递id,同时 名字和介绍至少传一个
        return tenantMono.filter(tenant -> StringUtils.hasText(tenant.getId())
                && (StringUtils.hasText(tenant.getName()) || StringUtils.hasText(tenant.getDescription())))
                .filter(dc3Tenant ->
                        //我们想再过滤一下,通过先查询有没有结果来决定是否需要更新
                        findById(dc3Tenant.getId()) != null)
                .flatMap(dc3Tenant -> {
                    //更新操作,将传递过来的数据更新到数据库中,前面的数据如果是空的mono,这里就不会执行
                    Map<SqlIdentifier, Object> columnsToUpdate = new LinkedHashMap<>();
                    if (dc3Tenant.getName() != null) {
                        columnsToUpdate.put(SqlIdentifier.unquoted("name"), dc3Tenant.getName());
                    }
                    if (dc3Tenant.getDescription() != null) {
                        columnsToUpdate.put(SqlIdentifier.unquoted("description"), dc3Tenant.getDescription());
                    }
                    return r2dbcEntityTemplate.update(Dc3Tenant.class)
                            .inTable("dc3_tenant")
                            .matching(Query.query(Criteria.where("id").is(dc3Tenant.getId())))
                            .apply(Update.from(columnsToUpdate));
                })
                .map(integer -> R.ok())
                .defaultIfEmpty(R.fail("缺少必要数据"))
                .doOnError(throwable -> R.fail("传递数据有误"));
    }

    @Override
    public Mono<Dc3Tenant> findWithUsers(String tenantId) {
        //查询租户的详细信息，
        return findById(tenantId)
                //根据租户的id查询用户id
                .flatMap(dc3Tenant -> {
                    return r2dbcEntityTemplate.select(Dc3TenantBind.class)
                            .from("dc3_tenant_bind")
                            .matching(Query.query(Criteria.where("tenant_id").is(tenantId)))
                            .all()
                            .map(Dc3TenantBind::getUserId)
                            .collectList()
                            .flatMap(userIds -> userClient.findByIdIn(userIds))
                            .map(user -> {
                                dc3Tenant.setUserList((List<Dc3User>) user.getData());
                                return dc3Tenant;
                            });
                });
    }

    @Override
    public Mono<Integer> findCountByTenantIdAndUserId(String tenantId, List<String> userIds) {
        return r2dbcEntityTemplate.select(Dc3TenantBind.class).from("dc3_tenant_bind")
                .matching(Query.query(Criteria.where("tenant_id")
                        .is(tenantId)
                        .and(Criteria.where("user_id").in(userIds))))
                .all()
                .collectList()
                .map(dc3TenantBinds -> dc3TenantBinds.size());
    }

    @Override
    public Mono<R<Object>> bindUser2Tenant(Mono<Dc3TenantBind> dc3TenantBind) {
        return dc3TenantBind
                .flatMap(dc3TenantBind1 -> {
                    //查询是否存在绑定关系
                    ArrayList<String> list = new ArrayList<>();
                    list.add(dc3TenantBind1.getUserId());
                    return findCountByTenantIdAndUserId(dc3TenantBind1.getTenantId(), list)
                            .filter(count -> count == 0)
                            .map(i -> {
                                //添加绑定关系
                                dc3TenantBind1.setId(UUID.randomUUID().toString(true));
                                return r2dbcEntityTemplate
                                        .insert(Dc3TenantBind.class)
                                        .into("dc3_tenant_bind")
                                        .using(dc3TenantBind1).subscribe();
                            });
                })
                .map(dc3TenantBind1 -> R.ok())
                .defaultIfEmpty(R.fail("绑定错误"));
    }
}
